perm filename FREEFO.LES[UP,DOC]4 blob sn#104049 filedate 1974-05-30 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00003 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	BEGIN  "FREEFOROL"
C00009 00003	DEFINE NOVA="25"  COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS
C00014 ENDMK
C⊗;
BEGIN  "FREEFOROL"
	COMMENT  10 MARCH 1973

OPERATION

Freeforol is a text macro processor that can be used to generate form
letters  and other fill-in-the-blanks text.  If you say  R FREEFO, it
types a "*" and expects a keyboard input of the form
    <source file list>
or
    <destination file>←<source file list>
where the  <source  file list>  consists of  one or  more file  names
separated  by  commas.    These  must  be  text  files  and  will  be
effectively concatenated in the order given.

The output goes to the destination file,  if  given.   Otherwise  one
copy of the output is spooled.

PROCESS

The source file(s) are expected to contain a text macro  followed  by
one or more argument lists.  This program outputs a copy of the macro
for each arguments list, with arguments substituted.

The first character of the source file (neglecting any directory page
or  line numbers)  is  taken as  the macro  delimiter.   If  the next
character is  identical,  then  everything  from there  to  the  next
occurence of the delimiter character is treated as a "prefix", and is
placed at the front of the output file.

Whether or not there  is a  prefix, the macro  definition comes next.
Each place where an argument is to be substituted  is designated by a
<delimiter><parameter>  pair,    where  the  <parameter> may  be  "1"
through "9" (representing the first  through ninth arguments) or  "A"
through "J" (10th through 20th arguments).  The macro ends with
    <delimiter><separator><delimeter>
where the <separator> is any non-<parameter>.

The argument lists begin on the next line, which can of course be  in
a  different file if you wish.  Either of two formats may be used, as
follows.

1) If the <separator> is <CR> or <CR><LF>, then each line of text  is
interpreted  as  an  argument  and  the list is terminated by a blank
line.   This format is convenient for lists of addressees that are to
be inserted in a form letter.

2) If the separator is any other character, then each line of text is
interpreted  as  a  list  whose  arguments  are  separated   by   the
<separator>.

Thus, the general form of the source file(s) is
	<delimiter>[<delimiter> <prefix> <delimiter>]
	<macro body, including parameters>
	<delimiter><separator><delimiter>
	<argument lists>
where the bracketed terms are optional.

EXAMPLES

For example, suppose the following source file were used:
_____________________________________________________________________
⊗⊗.require "foo.baz" source_file
⊗		      Veterans Administration
                          Washington, D. C.
						          4 July 1984
⊗1 ⊗2
⊗3
⊗4

Dear ⊗1:

Our office has  recently  determined  that  you  qualify  for  a  10%
disability.   Congratulations ...

⊗
⊗
John
Brown
Arlington National Cemetery
Arlington, Virginia

Speedy
Gonzales
Blue Fox Restaurant
Tijuana, Mexico

_____________________________________________________________________

Then "⊗"  is the  delimiter and the  separator is  <CR><LF>,  so  the
output file would take the form:
_____________________________________________________________________
.require "foo.baz" source_file
		      Veterans Administration
...
John Brown
Arlington National Cemetery
Arlington, Virginia

Dear John:
....

		      Veterans Administration
...
Speedy Gonzales
Blue Fox Restaurant
...
_____________________________________________________________________

Alternatively, if the source file were:
_____________________________________________________________________
@
	This is a@2 macro processor,
	but it is @1.

@,@
fairly general, simple
rather easy to understand,n elegant
_____________________________________________________________________

then "@" is the delimiter  and "," is the separator, and  there is no
prefix, so this produces:
	This is a simple macro processor,
	but it is fairly general.

	This is an elegant macro processor,
	but it is fairly easy to understand.

FEATURES

Several features should be noted.
1)  Any form feeds in the macro body are preserved, but they are
    ignored in the argument lists.
2)  Excess or unused arguments are ignored, so you can put comments
    in the argument lists that will not show up in the output.
3)  Arguments that are omitted are automatically set to NULL.
;
DEFINE NOVA="25";  COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS;
DEFINE ARGS="20";  COMMENT THE MAXIMUM NUMBER OF AGRUMENTS;

DEFINE THIS="COMMENT", CR="'15", LF="'12", HT="'11", FF="'14",↓="'15&'12",
	THRU="STEP 1 UNTIL", ⊃="←1 STEP 1 UNTIL", LN="LENGTH",
	ERR(MES)="BEGIN OUTSTR(""MES""&↓); CALL(0,""EXIT"") END";
DEFINE INCH="1", OUCH="2", LINE="1", MARK="2", CHAR="3",AMP="4",FORMER="5";

REQUIRE 2000 STRING_SPACE;
STRING ARRAY TEXT[1:NOVA];  THIS HOLDS SEGMENTS OF MACRO TEXT;
INTEGER ARRAY PARG[1:NOVA];
STRING ARRAY VAL[1:ARGS];  THIS HOLDS CURRENT ARGUMENT VALUES;
STRING S,S0,SOURCE,DEST;
INTEGER BRK, EOF,T, V, I,FLAG;

EXTERNAL PROCEDURE SPOOL(STRING FILE; INTEGER IOCHAN,FLAGS);

PROCEDURE LOOK;  BEGIN  BOOLEAN FL;  STRING FILE;
	FILE←SCAN(SOURCE,AMP,BRK);
	IF BRK="[" THEN FILE←FILE&"["&SCAN(SOURCE,AMP,BRK)&","&SCAN(SOURCE,AMP,BRK);
	LOOKUP(INCH,FILE,FL);
	IF FL THEN BEGIN OUTSTR(FILE&" not found"&↓); CALL(0,"EXIT") END;
	END;

STRING SIMPLE PROCEDURE READ;	BEGIN  THIS READS THE SOURCE FILE(S);
	STRING RS,SS;	RS←INPUT(INCH,CHAR);
	IF ¬EOF ∨ LN(SOURCE)=0 THEN RETURN(RS);
	CLOSE(INCH);  LOOK;  SS←INPUT(INCH,CHAR);
	IF EQU(SS[1 TO 9],"COMMENT ⊗") THEN BEGIN "TV FILE"
		DO SS←INPUT(INCH,FORMER) UNTIL BRK=FF ∨ EOF;
		SS←INPUT(INCH,CHAR);
		END;
	RETURN(RS&SS)
	END;

	THIS INITIALIZES I/O;
OUTSTR("*");
OPEN(INCH,"DSK",1,2,0,4000,BRK,EOF);  OPEN(OUCH,"DSK",1,0,2,0,BRK,EOF);
SETBREAK(AMP,"←",NULL,"INS");	SETBREAK(FORMER,FF,NULL,"INS");
DEST←SCAN(SOURCE←INCHWL,AMP,BRK);	THIS READS THE FILE NAMES;
IF BRK="←" THEN BEGIN "destination file"
	ENTER(OUCH,DEST,FLAG);	DEST←NULL;
	END
    ELSE BEGIN "spool it"
	SOURCE←DEST; THIS RESTORES SOURCE FILE NAMES;
	DEST←"FREEF";  I←"0"-1;
	DO BEGIN LOOKUP(OUCH,DEST&(I←I+1)&".TMP",FLAG);  CLOSE(OUCH) END
	    UNTIL FLAG;
	ENTER(OUCH,DEST←DEST&I&".TMP",FLAG);
	END;
IF FLAG THEN ERR(Destination file cannot be written);
SETBREAK(AMP,"[,"," 	","INS");  LOOK;

	THIS READS AND SEGMENTS THE TEXT MACRO;
SETBREAK(CHAR,NULL,NULL,"XNA");
IF (I←READ)="C" THEN BEGIN  "FLUSH DIRECTORY"
	DO I←INPUT(INCH,FORMER) UNTIL BRK=FF ∨ EOF;
	I←READ;
	END;
SETBREAK(CHAR,I,NULL,"INS");

IF LN(S0←READ)=0 THEN BEGIN "PREFIX"
	DO OUT(OUCH,INPUT(INCH,CHAR)) UNTIL BRK=I ∨ EOF;
	S0←READ;
	END;
T←0;
DO V←LOP(TEXT[T←T+1]←READ) UNTIL (I←PARG[T]←V-(IF "0"<V≤"9" THEN "0"
    ELSE IF "A"≤V THEN "A"-10 ELSE "A"))≤0 ∨ I>ARGS;

	THIS FINDS THE SEPARATORS AND TERMINATOR;
SETBREAK(MARK,V,NULL,"INS");  SETBREAK(CHAR,LF,CR&FF,"INS");
I←READ;		THIS FLUSHES THE REST OF THE LINE;

DO BEGIN	THIS SCANS ARGUMENT LISTS AND OUTPUTS TEXT;
	IF V=CR THEN BEGIN "1 ARGUMENT PER LINE"
		FOR I ⊃ ARGS DO IF LN(VAL[I]←READ)=0 THEN DONE;
		END
	    ELSE BEGIN "SET OF ARGUMENTS PER LINE"
		S←READ;
		FOR I ⊃ ARGS DO IF LN(VAL[I]←SCAN(S,MARK,BRK))=0 THEN DONE;
		END;
	IF EOF ∧ I=1 THEN DONE;
	FOR I←I THRU ARGS DO VAL[I]←NULL;
	OUT(OUCH,S0);
	FOR I ⊃ T-1 DO BEGIN OUT(OUCH,VAL[PARG[I]]); OUT(OUCH,TEXT[I]) END;
	END UNTIL EOF;
RELEASE(INCH);  RELEASE(OUCH);
IF LN(DEST) THEN SPOOL(OUCH,DEST,1);
END;